//
//  unionfs_Filesystem.m
//  unionfs
//
//  Created by doug on 2008-12-20.
//
#import <sys/xattr.h>
#import <sys/stat.h>

#import "unionfs_Filesystem.h"
#import <MacFUSE/MacFUSE.h>
#import <Foundation/NSArray.h>


// Category on NSError to  simplify creating an NSError based on posix errno.
@interface NSError (POSIX)
+ (NSError *)errorWithPOSIXCode:(int)code;
@end
@implementation NSError (POSIX)
+ (NSError *)errorWithPOSIXCode:(int) code {
	return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil];
}
@end

// NOTE: It is fine to remove the below sections that are marked as 'Optional'.

// The core set of file system operations. This class will serve as the delegate
// for GMUserFileSystemFilesystem. For more details, see the section on 
// GMUserFileSystemOperations found in the documentation at:
// http://macfuse.googlecode.com/svn/trunk/core/sdk-objc/Documentation/index.html


@implementation unionfs_Filesystem


- (id)init {
	return [self initWithPaths:	[[NSArray alloc]init]];
}

- (id)initWithPaths:(NSArray*) apaths {
	if ((self = [super init])) {
		paths = [NSArray arrayWithArray: apaths]; 
		return self;
	}
	return nil;
}

- (NSArray *) possibleUnionPaths:(NSString *) path{
	NSMutableArray * array = [[NSMutableArray alloc] init];
	
	for (  NSString * rootPath in paths) 
	{
		
		NSString * fullPath = [rootPath stringByAppendingString: path];
		[array addObject:fullPath];
	}
	return array;
	
}

#pragma mark Directory Contents

- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error {
	
	NSMutableArray * contents  = [[NSMutableArray alloc] init];
	
	for(NSString *underlying_path in [self possibleUnionPaths: path])
	{
		NSArray * path_contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:underlying_path error:error];
		[contents addObjectsFromArray: path_contents];
	}
	
	return contents;
}
#pragma mark Getting Attributes

- (NSDictionary *)attributesOfItemAtPath:(NSString *)path
                                userData:(id)userData
                                   error:(NSError **)error {
	
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
		NSDictionary* attribs = 
		[[NSFileManager defaultManager] attributesOfItemAtPath:underlying_path error:error];
		if(attribs)
		{
			return attribs;
		}
	}
	return nil;
}

- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path
                                          error:(NSError **)error {
	
	
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
		
		NSDictionary* d =
		[[NSFileManager defaultManager] attributesOfFileSystemForPath:underlying_path error:error];
		if (d) {
			NSMutableDictionary* attribs = [NSMutableDictionary dictionaryWithDictionary:d];
			[attribs setObject:[NSNumber numberWithBool:YES]
						forKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey];
			return attribs;
			
		}
		
	}
	return nil;
}

#pragma mark File Contents

// TODO: There are two ways to support reading of file data. With the contentsAtPath
// method you must return the full contents of the file with each invocation. For
// a more complex (or efficient) file system, consider supporting the openFileAtPath:,
// releaseFileAtPath:, and readFileAtPath: delegate methods.

- (BOOL)openFileAtPath:(NSString *)path 
                  mode:(int)mode
              userData:(id *)userData
                 error:(NSError **)error {
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
	   	int fd = open([underlying_path UTF8String], mode);
		if ( fd >= 0 ) {
			*userData = [NSNumber numberWithLong:fd];
			return YES;
		}
	}
	//if there are multiple directories, return the failure associated with the last one attempted.
	
	*error = [NSError errorWithPOSIXCode:errno];
	return NO;
	
}

- (void)releaseFileAtPath:(NSString *)path userData:(id)userData {
	
	NSNumber* num = (NSNumber *)userData;
	int fd = [num longValue];
	close(fd);
}

- (int)readFileAtPath:(NSString *)path 
             userData:(id)userData
               buffer:(char *)buffer 
                 size:(size_t)size 
               offset:(off_t)offset
                error:(NSError **)error {
	NSNumber* num = (NSNumber *)userData;
	int fd = [num longValue];
	int ret = pread(fd, buffer, size, offset);
	if ( ret < 0 ) {
		*error = [NSError errorWithPOSIXCode:errno];
		return -1;
	}
	return ret;
	
}



#pragma mark Symbolic Links (Optional)

- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path
                                        error:(NSError **)error {
	
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
		
		NSString *str =  [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:underlying_path error:error];
		if(str)
		{
			return str;
		}
	}
	return nil;
}

#pragma mark Extended Attributes (Optional)

- (NSArray *)extendedAttributesOfItemAtPath:(NSString *)path error:(NSError **)error {
	
	NSMutableArray* contents = nil;
	
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
		
		ssize_t size = listxattr([underlying_path UTF8String], nil, 0, 0);
		if ( size < 0 ) {
			*error = [NSError errorWithPOSIXCode:errno];
			continue;
		}
		NSMutableData* data = [NSMutableData dataWithLength:size];
		size = listxattr([underlying_path UTF8String], [data mutableBytes], [data length], 0);
		if ( size < 0 ) {
			*error = [NSError errorWithPOSIXCode:errno];
			continue;
		}
		contents = [NSMutableArray array];
		char* ptr = (char *)[data bytes];
		while ( ptr < ((char *)[data bytes] + size) ) {
			NSString* s = [NSString stringWithUTF8String:ptr];
			[contents addObject:s];
			ptr += ([s length] + 1);
		}
		break;
	}
	return contents;
	
}

- (NSData *)valueOfExtendedAttribute:(NSString *)name 
                        ofItemAtPath:(NSString *)path
                            position:(off_t)position
                               error:(NSError **)error {
	NSMutableData* data = nil;
	for(NSString *underlying_path in [self possibleUnionPaths: path]) {
		
		
		ssize_t size = getxattr([underlying_path UTF8String], [name UTF8String], nil, 0,
								position, 0);
		
		
		if ( size < 0 ) {
			*error = [NSError errorWithPOSIXCode:errno];
			continue;
		}
		data = [NSMutableData dataWithLength:size];
		size = getxattr([underlying_path UTF8String], [name UTF8String], 
						[data mutableBytes], [data length],
						position, 0);
		if ( size < 0 ) {
			*error = [NSError errorWithPOSIXCode:errno];
			continue;
		}
		break;
	}
	return data;
	
}

#pragma mark FinderInfo and ResourceFork (Optional)

- (NSDictionary *)finderAttributesAtPath:(NSString *)path 
                                   error:(NSError **)error {
	return [NSDictionary dictionary];
}

- (NSDictionary *)resourceAttributesAtPath:(NSString *)path
                                     error:(NSError **)error {
	return [NSDictionary dictionary];
}

@end
